package org.kdev.wtf.adapter;

import static org.kdev.wtf.WtfManager.adapter;

import com.google.gson.Gson;
import com.thoughtworks.selenium.SeleniumException;

/**
 * EstJS 2.2 library adapter
 */
public class ExtAdapter
{
   private static final String UPDATE_INDICATOR_TEXT = "Loading...";
   private static final String DATA_LOAD_TEXT = "Wczytywanie danych...";

   public static void clickCellAndWaitForPage(String gridId, String cellText, int colIndex)
   {
      clickGridCellByColIndex(gridId, cellText, colIndex);
      adapter.waitForPageToLoad();
   }

   public static void clickCellAndWaitForLoading(String gridId, String cellText)
   {
      clickGridCell(gridId, cellText);
      waitForLoading();
   }

   /**
    * Klika w znajdujacy sie w gridzie przycisk "Refresh"
    */
   public static void clickGridRefreshButton()
   {
      String locator = "xpath=//button[@class='x-btn-text x-tbar-loading']";
      if (!adapter.isElementPresent(locator))
         throw new SeleniumException("Can not click on refresh button");

      adapter.click(locator);
      waitForLoading();
   }

   /**
    * Klika na komorke o tekscie cellText w gridzie w divie o id=gridId por.
    * clickGridCellByGridID()
    * 
    * @param gridId
    * @param cellText
    */
   public static void clickGridCell(String gridId, String cellText)
   {
      String gridDiv = "xpath=//div[@id='" + gridId + "']";
      // dla grida grupujacego 10 divow przerwy, dla zwyklego 8
      String divs = "//div/div/div/div/div/div/div/div";
      String cell = "/table/tbody/tr/td/div[text()='" + cellText + "']";
      String cellXpath = gridDiv + divs + cell;
      if (!adapter.isElementPresent(gridId))
         throw new SeleniumException("Nie ma elementu grida o ID:'" + gridId + "'");

      if (!adapter.isElementPresent(cellXpath))
         throw new SeleniumException("Nie ma komorki gdzie XPath:'" + cellXpath + "'");

      adapter.click(cellXpath);
   }

   /**
    * Zwraca id wiersza w gridzie Numeracja od 1..
    * 
    * @param gridId
    * @param row
    * @return
    */
   public static String getGridCellId(String gridId, int row)
   {

      String script = String.format("window.Kfn.wtf.grid.getRowId('%s', %d);", gridId, row);

      String rowId = adapter.getEval(script);

      if (!adapter.isElementPresent("xpath=//div[@id='" + rowId + "']"))
         throw new SeleniumException("Grid " + gridId + " nie ma wiersza nr " + row);

      return rowId;
   }

   /**
    * Klika na komorke o tekscie cellText w gridzie Extowym o id=gridId
    * Numeracja kolumn od 0... por. clickGridCell() -> Używa metod EXTJS do
    * lokalizowania grida
    * 
    * @param gridId
    * @param cellText
    */

   public static void clickGridCellByColIndex(String gridId, String cellText, int colIndex)
   {

      if (!adapter.isElementPresent(gridId))
         throw new SeleniumException("Nie ma elementu grida o ID:'" + gridId + "'");

      // cellText = cellText.replace("\\","\\\\").replace("'", "\\'");

      String script = "var grid = window.Ext.getCmp('" + gridId + "');\n"
            + "var store = grid.getStore();\n"
            + "var dataIndex = grid.getColumnModel().getDataIndex(" + new Integer(colIndex)
            + ");\n" + "var index = store.find(dataIndex, /^" + cellText + "$/);\n"
            + "window.Ext.get(grid.getView().getRow(index)).id;\n";
      /*
       * JAVASCRIPT: var grid = Ext.getCmp('gridListaProjektow'); var store =
       * grid.getStore(); var dataIndex = grid.getColumnModel().getDataIndex(0);
       * var index = store.find( dataIndex, 'Szkoła Podstawowa SP9 - TEST');
       * //window.Ext.MessageBox.alert(index,index);
       * window.Ext.get(grid.getView().getRow(index)).id
       */

      String rowXpath = "xpath=//div[@id='" + adapter.getEval(script) + "']";

      if (!adapter.isElementPresent(rowXpath))
         throw new SeleniumException("Nie ma komorki gdzie XPath:'" + rowXpath + "'");

      adapter.click(rowXpath);
   }

   public static void clickTabPanel(String labelText)
   {
      adapter.mouseDown("xpath=//a/em/span/span[text()='" + labelText + "']");
   }

   public static boolean isColumnPresent(String gridId, String colName)
   {
      String script = String.format("window.Kfn.wtf.grid.isColumnPresent('%s', '%s');", gridId,
            colName);

      if (adapter.getEval(script).equals("true"))
         return true;
      return false;
   }

   public static String convertToJson(String result)
   {
      /*
       * JavaScriptowa metoda toSource() zwraca polskie znaki jako np. "\u0105",
       * dlatego zamieniamy te ciągi na odpowiednie literki
       */
      result = result.replaceAll("\\\\u0105", "ą");
      result = result.replaceAll("\\\\u0107", "ć");
      result = result.replaceAll("\\\\u0119", "ę");
      result = result.replaceAll("\\\\u0142", "ł");
      result = result.replaceAll("\\\\u0144", "ń");
      result = result.replaceAll("\\\\xF3", "ó");
      result = result.replaceAll("\\\\u015B", "ś");
      result = result.replaceAll("\\\\u017A", "ź");
      result = result.replaceAll("\\\\u017C", "ż");

      result = result.replaceAll("\\\\u0104", "Ą");
      result = result.replaceAll("\\\\u0106", "Ć");
      result = result.replaceAll("\\\\u0118", "Ę");
      result = result.replaceAll("\\\\u0141", "Ł");
      result = result.replaceAll("\\\\u0143", "Ń");
      result = result.replaceAll("\\\\xD3", "Ó");
      result = result.replaceAll("\\\\u015A", "Ś");
      result = result.replaceAll("\\\\u0179", "Ź");
      result = result.replaceAll("\\\\u017B", "Ż");

      return result;
   }

   public static String[][] getGridMultipleColumnTypesByName(String gridId, String[] colName)
   {
      String columns = "";
      for (String s : colName)
      {
         // is number
         if (isNumber(s))
            columns = columns + s + ",";
         else
            columns = columns + "'" + s + "',";
      }

      columns = columns.substring(0, columns.length() - 1);

      String script = String.format(
            "window.Kfn.wtf.grid.getMultipleColumnDataTypesByNameJson('%s', %s);", gridId, columns);
      String result = adapter.getEval(script);

      result = convertToJson(result);

      Gson gson = new Gson();

      return gson.fromJson(result, String[][].class);
   }

   public static String[][] getGridMultipleColumnData(String gridId, int[] colIndex)
   {
      String columns = "";
      for (int i : colIndex)
      {
         columns = columns + String.valueOf(i) + ",";
      }

      columns = columns.substring(0, columns.length() - 1);

      String script = String.format("window.Kfn.wtf.grid.getMultipleColumnDataJson('%s', %s);",
            gridId, columns);
      String result = adapter.getEval(script);

      result = convertToJson(result);
      Gson gson = new Gson();

      return gson.fromJson(result, String[][].class);
   }

   public static String[][] getGridMultipleColumnDataByName(String gridId, String[] colName)
   {
      String columns = "";
      for (String s : colName)
      {
         // is number
         if (isNumber(s))
            columns = columns + s + ",";
         else
            columns = columns + "'" + s + "',";
      }

      columns = columns.substring(0, columns.length() - 1);

      String script = String.format(
            "window.Kfn.wtf.grid.getMultipleColumnDataByNameJson('%s', %s);", gridId, columns);
      String result = adapter.getEval(script);

      result = convertToJson(result);

      Gson gson = new Gson();

      return gson.fromJson(result, String[][].class);
   }

   public static String[] getGridColumnData(String gridId, int colIndex)
   {
      String script = String.format("window.Kfn.wtf.grid.getColumnDataJson('%s', %d);", gridId,
            colIndex);
      String result = adapter.getEval(script);

      result = convertToJson(result);

      Gson gson = new Gson();

      return gson.fromJson(result, String[].class);
   }

   public static String[] getGridColumnData(String gridId, String colName)
   {
      String script = String.format("window.Kfn.wtf.grid.getColumnDataByNameJson('%s', '%s');",
            gridId, colName);
      String result = adapter.getEval(script);

      result = convertToJson(result);

      Gson gson = new Gson();

      return gson.fromJson(result, String[].class);
   }

   public static String[] getGridColumnDataTypes(String gridId, int colIndex)
   {
      String script = String.format("window.Kfn.wtf.grid.getColumnDataTypesJson('%s', %d);",
            gridId, colIndex);
      String result = adapter.getEval(script);

      result = convertToJson(result);

      Gson gson = new Gson();

      return gson.fromJson(result, String[].class);
   }

   public static String[] getGridColumnDataTypes(String gridId, String colName)
   {
      String script = String.format(
            "window.Kfn.wtf.grid.getColumnDataTypesByNameJson('%s', '%s');", gridId, colName);
      String result = adapter.getEval(script);

      result = convertToJson(result);

      Gson gson = new Gson();

      return gson.fromJson(result, String[].class);
   }

   /**
    * @deprecated to jest koszmar. Zastap przez getGridVisibleDataColumnNames
    * @param gridId
    * @return
    */
   public static String[] getGridColumnHeaders(String gridId)
   {
      int n = ExtAdapter.getGridNumberOfColumns(gridId);

      String[] headers = new String[n];

      for (int i = 1; i <= n; i++)
      {
         String script = String.format("window.Kfn.wtf.grid.getRealColName('%s', %d);", gridId, i);
         headers[i - 1] = adapter.getEval(script);
      }

      return headers;
   }

   public static String[] getGridVisibleDataColumnNames(String gridId)
   {
      String script = String.format("window.Kfn.wtf.grid.getVisibleDataColumnNames('%s');", gridId);
      String headers = adapter.getEval(script);

      String[] headersTable = headers.split(":");

      return headersTable;
   }

   public static int getGridNumberOfRows(String gridId)
   {
      String script = String.format("window.Kfn.wtf.grid.getNumberOfRows('%s');", gridId);

      return new Integer(adapter.getEval(script));
   }

   public static int getGridNumberOfColumns(String gridId)
   {
      String script = String.format("window.Kfn.wtf.grid.getNumberOfColumns('%s');", gridId);

      return new Integer(adapter.getEval(script));
   }

   /**
    * Numeracja od 1..
    * 
    * @param gridId
    * @param row
    * @param col
    * @return text value of grid cell
    */
   public static String getGridCellValue(String gridId, int row, int col)
   {
      String script = String.format("window.Kfn.wtf.grid.getCellValue('%s', %d, %d);", gridId, row,
            col);

      return adapter.getEval(script);
   }

   public static String getGridCellValueByName(String gridId, int row, String colName)
   {
      String script = String.format("window.Kfn.wtf.grid.getCellValueByName('%s', %d, '%s');",
            gridId, row, colName);

      return adapter.getEval(script);
   }

   public static String getGridCellValueType(String gridId, int row, int col)
   {
      String script = String.format("window.Kfn.wtf.grid.getCellValueType('%s', %d, %d);", gridId,
            row, col);

      return adapter.getEval(script);
   }

   public static String getGridCellValueTypeByName(String gridId, int row, String colName)
   {
      String script = String.format("window.Kfn.wtf.grid.getCellValueTypeByName('%s', %d, '%s');",
            gridId, row, colName);

      return adapter.getEval(script);
   }

   /**
    * Combo Box with type search enabled.
    * 
    * @param locator
    * @param value
    */
   public static void selectComboBox(String locator, String value)
   {
      if (!adapter.isElementPresent(locator))
         throw new SeleniumException("There is no Ext Combo Box where locator='" + locator + "'");

      String optionLocator = "//div[@class='x-layer x-combo-list']"
            + "/div[@class='x-combo-list-inner']" + "/div[text()='" + value + "']";
      adapter.mouseDown(locator);
      adapter.click(optionLocator);
   }

   /**
    * ComboBox with drop down list.
    * 
    * @param extInputName
    * @param option
    */
   public static void selectComboBoxList(String extInputName, String option)
   {
      String extInputLocator = "xpath=//input[@name='" + extInputName + "']";
      if (!adapter.isElementPresent(extInputLocator))
         throw new SeleniumException("There is no Ext Combo Box where name='" + extInputName + "'");

      String optionXpath = "//div[@class='x-layer x-combo-list']"
            + "/div[@class='x-combo-list-inner']//*[text()='" + option + "']";

      adapter.click(extInputLocator + "/following-sibling::img");
      adapter.waitForElement(optionXpath);
      adapter.click(optionXpath);
   }

   public static void selectMultiselectGrouped(String[] values, boolean select)
   {
      String selectItem = "";
      if (!select)
         selectItem = " ux-mselect-selected";

      adapter.controlKeyDown();
      for (int i = 0; i < values.length; i++)
      {
         String optionLocator = "//div[@class='ux-mselect-item x-unselectable" + selectItem
               + "']/span[@class='multiselect_item' and text()='" + values[i] + "']";
         adapter.click(optionLocator);
      }
      adapter.controlKeyUp();
   }

   public static void selectMultiselect(String[] values, boolean select)
   {
      String selectItem = "";
      if (!select)
         selectItem = " ux-mselect-selected";

      adapter.controlKeyDown();
      for (int i = 0; i < values.length; i++)
      {
         String optionLocator = "//div[@class='ux-mselect-item x-unselectable" + selectItem
               + "' and text()='" + values[i] + "']";

         adapter.click(optionLocator);
      }
      adapter.controlKeyUp();
   }

   /**
    * @todo: 2008-08-22 - dawid: rozbudowac
    * @param nodeLabel
    */
   public static void clickTreeNode(String nodeLabel)
   {
      adapter.clickLabel(nodeLabel);
   }

   public static void waitForLoading()
   {
      /**
       * Ext.Msg('Loading...');
       */
      String selCondition;
      String elementPleaseWait = "xpath=//div[@class='x-progress-text x-progress-text-back']";

      if (adapter.isElementPresent(elementPleaseWait))
      {
         selCondition = "!selenium.isVisible(\"" + elementPleaseWait + "\")";
         adapter.waitForCondition(selCondition);
      }

      selCondition = "!selenium.isTextPresent('" + UPDATE_INDICATOR_TEXT + "')";
      adapter.waitForCondition(selCondition);

      /**
       * Ext.Msg('Wczytywanie danych...');
       */
      elementPleaseWait = "xpath=//div[@class='x-mask-loading']";

      if (adapter.isElementPresent(elementPleaseWait))
      {
         selCondition = "!selenium.isVisible(\"" + elementPleaseWait + "\")";
         adapter.waitForCondition(selCondition);
      }

      selCondition = "!selenium.isTextPresent('" + DATA_LOAD_TEXT + "')";
      adapter.waitForCondition(selCondition);
   }

   public static boolean isMessageWindowPresent(String title)
   {
      String result = adapter
            .getEval("(window.Ext.Msg.isVisible() && window.Ext.Msg.getDialog().title == '" + title
                  + "')");
      return result.equals("true");
   }

   public static void waitForMessageDisappear(String title)
   {
      adapter.waitForCondition("var window = selenium.browserbot.getCurrentWindow();\n"
            + "!(window.Ext.Msg.isVisible() && window.Ext.Msg.getDialog().title == '" + title
            + "')");
   }

   public static boolean isComponentDisabled(String extId)
   {
      String res = adapter.getEval("window.Ext.getCmp('" + extId + "').disabled;");
      if (res.equals("true"))
         return true;
      else if (res.equals("false"))
         return false;
      else
         throw new SeleniumException("isComponentDisabled(): getEval failed with return value: "
               + res);
   }

   public static void waitForMessageAppear(String title)
   {
      adapter
            .waitForCondition("var window = selenium.browserbot.getCurrentWindow();\n"
                  + "(window.Ext.Msg.isVisible() && window.Ext.Msg.getDialog().title == '" + title
                  + "')");
   }

   public static void clickAndWaitForLoading(String buttonText)
   {
      adapter.clickButton(buttonText);
      String selCondition = "selenium.isElementPresent(\"//div[@class='ext-el-mask-msg x-mask-loading']/div[text()='"
            + UPDATE_INDICATOR_TEXT + "']\")";
      adapter.waitForCondition(selCondition);
      String selCondition2 = "!selenium.isElementPresent(\"//div[@class='ext-el-mask-msg x-mask-loading']/div[text()='"
            + UPDATE_INDICATOR_TEXT + "']\")";
      adapter.waitForCondition(selCondition2);
   }

   public static void waitForWindow(String windowId)
   {
      try
      {
         adapter.waitForElement(windowId);
      }
      catch (SeleniumException e)
      {
         throw new SeleniumException("Can not open Ext window: '" + windowId + "'", e);
      }
   }

   /**
    * zwraca nr wiersza z podana wartoscia w kolumnie nr col numeracja od 1.. 0
    * - brak szukanego wiersza
    */
   public static int findRowWithValueInGrid(String gridId, String cellValue, int colIndex)
   {

      String script = String.format("window.Kfn.wtf.grid.getRowWithValue('%s', '%s', %d);", gridId,
            cellValue, colIndex);

      return new Integer(adapter.getEval(script));
   }

   public static void waitForValueInGrid(String gridId, String cellValue, int colIndex)
   {
      String script = String.format("window.Kfn.wtf.grid.getRowWithValue('%s', '%s', %d) >= 1;",
            gridId, cellValue, colIndex);

      adapter.waitForCondition(script);
   }

   public static void selectByName(String extInputName, String option)
   {
      if (option == null)
         return;

      String optionXpath = "xpath=//div[@class='x-layer x-combo-list' and contains(@style, 'visible;')]"
            + "/div[@class='x-combo-list-inner']/div[text()='" + option + "']";

      String extInputLocator = "xpath=//input[@name='" + extInputName + "']";
      if (!adapter.isElementPresent(extInputLocator))
         throw new SeleniumException("Brak selectExt [" + extInputName + "]");
      adapter.click(extInputLocator + "/following-sibling::*[2]");

      if (!adapter.isElementPresent(optionXpath))
      {
         optionXpath = "xpath=//div[@class='x-layer x-combo-list ' and contains(@style, 'visible;')]"
               + "/div[@class='x-combo-list-inner']/div[text()='" + option + "']";
         if (!adapter.isElementPresent(optionXpath)) // z dodana spacja za
                                                     // 'x-combo-list'
            throw new SeleniumException("Brak opcji w selectExt [" + extInputName + ":" + option
                  + "]");
      }
      adapter.click(optionXpath);
   }

   public static void selectById(String extInputId, String option)
   {
      if (option == null)
         return;

      String optionXpath = "xpath=//div[@class='x-layer x-combo-list ' and contains(@style, 'visible;')]"
            + "/div[@class='x-combo-list-inner']/div[text()='" + option + "']";

      String extInputLocator = "xpath=//input[@id='" + extInputId + "']";
      if (!adapter.isElementPresent(extInputLocator))
         throw new SeleniumException("Brak selectExt [" + extInputId + "]");
      adapter.mouseDown(extInputLocator);

      if (!adapter.isElementPresent(optionXpath))
      {
         throw new SeleniumException("Brak opcji w selectExt [" + extInputId + ":" + option + "]");
      }
      adapter.click(optionXpath);
   }

   /**
    * Klika na Zakladke Exta
    * 
    * @todo uzyc bardziej dopasowanego zapytania xpath
    * @param labelText
    */
   public static void clickTab(String labelText)
   {
      adapter.mouseDown("xpath=//span[text()='" + labelText + "']");
   }

   /**
    * XXX Stworz ogolny PbzProxy i tam przenies...
    * 
    * @param cellId
    */
   public static void clickEdytujInCell(String cellId)
   {
      adapter.click("xpath=//div[@id='" + cellId + "']/table/tbody/tr//div/img[@title='Edytuj']");
   }

   public static void clickUsunInCell(String cellId)
   {
      adapter.click("xpath=//div[@id='" + cellId + "']/table/tbody/tr//div/img[@title='Usuń']");
   }

   public static void clickEdytujInCellExt3(String cellId)
   {
      adapter.click("xpath=//div[@id='" + cellId
            + "']/table/tbody/tr//div/img[@class='x-action-col-icon x-action-col-0  ']");
   }

   public static void clickUsunInCellExt3(String cellId)
   {
      adapter.click("xpath=//div[@id='" + cellId
            + "']/table/tbody/tr//div/img[@class='x-action-col-icon x-action-col-1  ']");
   }

   public static void clickFirstTitleInCellExt3(String cellId)
   {
      adapter.click("xpath=//div[@id='" + cellId
            + "']/table/tbody/tr//div/img[@class='x-action-col-icon x-action-col-0  ']");
   }

   public static void clickSecondTitleInCellExt3(String cellId)
   {
      adapter.click("xpath=//div[@id='" + cellId
            + "']/table/tbody/tr//div/img[@class='x-action-col-icon x-action-col-1  ']");
   }

   public static void clickThirdTitleInCellExt3(String cellId)
   {
      adapter.click("xpath=//div[@id='" + cellId
            + "']/table/tbody/tr//div/img[@class='x-action-col-icon x-action-col-2  ']");
   }

   public static boolean isGridButtonEnabled(String gridId, String buttonText)
   {
      String script = String.format("window.Kfn.wtf.grid.isToolbarButtonEnabled('%s', '%s');",
            gridId, buttonText);

      if (adapter.getEval(script).equals("true"))
         return true;
      return false;
   }

   public static boolean isToolbarButtonEnabled(String toolbarId, String buttonText)
   {
      String script = String.format("window.Kfn.wtf.isToolbarButtonEnabled('%s', '%s');",
            toolbarId, buttonText);

      if (adapter.getEval(script).equals("true"))
         return true;
      return false;
   }

   public static void clickFirstRowInGrid(String gridId)
   {
      String xpath = "xpath=//div[@id='" + gridId + "']//div[position()=1]/table/tbody/tr/td/div";
      if (!adapter.isElementPresent(xpath))
         throw new SeleniumException("Brak lokalizacji: " + xpath);

      adapter.click(xpath);
      waitForLoading();

   }

   private static boolean isNumber(String s)
   {
      if (s.matches("((-|\\+)?[0-9]+(\\.[0-9]+)?)+"))
         return true;
      return false;
   }
}